package maquinasam.sam;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.HashMap;

public class SAM {
	private static final byte numRegisters = 16;

	private Registro[] registers;

	// Memoria de programa
	private ArrayList<Instruccion> codeSegment;

	// Memoria de datos
	private LinkedList<Integer> globalSegment;
	private LinkedList<RegistroActivacion> activationRegs;
	private LinkedList<Integer> heap;

	// Para almacenar las direcciones de las etiquetas definidas por el usuario
	private HashMap<String, Byte> userLabels; // etiqueta, direccion
	private HashMap<Byte, String> idTempLabels; // idenrif temporal, etiqueta
	private HashMap<String, Byte> stringTempLabels; // etiqueta, id temporal

	public SAM() {
		codeSegment = new ArrayList<Instruccion>();
		globalSegment = new LinkedList<Integer>();
		activationRegs = new LinkedList<RegistroActivacion>();
		heap = new LinkedList<Integer>();

		userLabels = new HashMap<String, Byte>();
		idTempLabels = new HashMap<Byte, String>();
		stringTempLabels = new HashMap<String, Byte>();

		registers = new Registro[numRegisters];
		for (byte i = Registro.CB; i < numRegisters; i++) {
			registers[i] = new Registro(i, (byte) 0);
		}
	}

	public void translateLabels() {
		for (int i = 0; i < codeSegment.size(); i++) {
			Instruccion inst = codeSegment.get(i);
			if ((inst.getOpcode() == Instruccion.CALL)
					&& (inst.getRegister() != Registro.PB)) {
				Byte idTemp = new Byte(inst.getOffset());
				String label = idTempLabels.get(idTemp);
				Byte dir = userLabels.get(label);
				System.out.println("Isntruccion: " + inst + ", \ndir: " + dir);
				codeSegment.get(i).setOffset(dir);
				codeSegment.get(i).setRegister(Registro.CB);
			}
		}
	}

	public void executeAll() {
		// inicializa
		for (byte i = Registro.CB; i < numRegisters; i++) {
			registers[i] = new Registro(i, (byte) 0);
		}

		while (true) {
			this.execute(codeSegment.get(registers[Registro.CP].getValue()));
		}
	}

	public void pushInstruction(Instruccion i) {
		codeSegment.add(i);
		registers[Registro.CP].incValue();
	}

	// inserta etiqueta asociada a su direccion (igual a CP)
	public void insertUserLabel(String nameLabel) { //
		if (!userLabels.containsKey(nameLabel))
			userLabels.put(nameLabel, registers[Registro.CP].getValue());
	}

	// inserta id temporal asociado a etiqueta
	public byte insertTempLabel(String nameLabel) {
		if (!(idTempLabels.containsKey(nameLabel))) {
			idTempLabels.put((byte) idTempLabels.size(), nameLabel); // se
																		// inserta
																		// con
																		// numero
																		// por
																		// ordenn
																		// de
																		// llegada
			stringTempLabels.put(nameLabel, (byte) stringTempLabels.size());
		}
		return stringTempLabels.get(nameLabel);
	}

	public void execute(Instruccion i) {
		System.out.println(i);

		switch (i.getOpcode()) {
		case Instruccion.LOAD:
			this.load(i);
			break;
		case Instruccion.LOADA:
			this.loada(i);
			break;
		case Instruccion.LOADI:
			this.loadi(i);
			break;
		case Instruccion.LOADL:
			this.loadl(i);
			break;
		case Instruccion.STORE:
			this.store(i);
			break;
		case Instruccion.STOREI:
			this.storei(i);
			break;
		case Instruccion.CALL:
			this.call(i);
			break;
		case Instruccion.CALLI:
			this.calli(i);
			break;
		case Instruccion.RETURN:
			this.returnar(i);
			break;
		case Instruccion.PUSH:
			this.push(i);
			break;
		case Instruccion.POP:
			this.pop(i);
			break;
		case Instruccion.JUMP:
			this.jump(i);
			break;
		case Instruccion.JUMPI:
			this.jumpi(i);
			break;
		case Instruccion.JUMPIF:
			this.jumpif(i);
			break;
		case Instruccion.HALT:
			this.halt(i);
			break;
		default:
			break;
		}
		registers[Registro.CP].incValue();
	}

	private void executePrimitive(Instruccion i) {
		switch (i.getOffset()) {
		case Primitiva.ID:
			this.id(i);
			break;
		case Primitiva.NOT:
			this.not(i);
			break;
		case Primitiva.AND:
			this.and(i);
			break;
		case Primitiva.OR:
			this.or(i);
			break;
		case Primitiva.INC:
			this.inc(i);
			break;
		case Primitiva.DEC:
			this.dec(i);
			break;
		case Primitiva.NEG:
			this.neg(i);
			break;
		case Primitiva.ADD:
			this.add(i);
			break;
		case Primitiva.SUB:
			this.sub(i);
			break;
		case Primitiva.MULT:
			this.mult(i);
			break;
		case Primitiva.DIV:
			this.div(i);
			break;
		case Primitiva.MOD:
			this.mod(i);
			break;
		case Primitiva.LT:
			this.lt(i);
			break;
		case Primitiva.LE:
			this.le(i);
			break;
		case Primitiva.GT:
			this.gt(i);
			break;
		case Primitiva.GE:
			this.ge(i);
			break;
		case Primitiva.EQ:
			this.eq(i);
			break;
		case Primitiva.NE:
			this.ne(i);
			break;
		case Primitiva.TO21:
			this.to21(i);
			break;
		case Primitiva.TO22:
			this.to22(i);
			break;
		case Primitiva.NEW:
			this.newar(i);
			break;
		case Primitiva.FREE:
			this.free(i);
			break;
		case Primitiva.GET:
			this.get(i);
			break;
		case Primitiva.PUT:
			this.put(i);
			break;
		default:
			break;
		}
	}

	// -------------------------------------------
	// Implementacion de primitivas
	// -------------------------------------------

	private void id(Instruccion i) {
		/*Integer op1;
		if (registers[Registro.LB].getValue() > 0) {
			op1 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			// Pushear de nuevo dos veces para replicar el tope de la pila
			activationRegs.get(registers[Registro.LB].getValue())
					.pushLocalData(op1);
			activationRegs.get(registers[Registro.LB].getValue())
					.pushLocalData(op1);
		} else {
			op1 = globalSegment.getLast();
			globalSegment.add(op1);
		}*/
	}

	private void not(Instruccion i) {
		Integer op1, res;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos de las zona de datos locales de dicho RA
			op1 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			res = ~op1;
			activationRegs.get(registers[Registro.LB].getValue())
					.pushLocalData(res);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto
			// obtendremos los operadores del segmento global de la pila
			op1 = globalSegment.removeLast();
			res = ~op1;
			globalSegment.add(res);
		}
	}

	private void and(Instruccion i) {
		Integer op1, op2, res;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos
			// de las zona de datos locales de dicho RA

			op1 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			// Este get de los registros de activacion indexando con el registro
			// LB es equivalente a un getLast()
			op2 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			res = op1 & op2;
			activationRegs.get(registers[Registro.LB].getValue())
					.pushLocalData(res);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto
			// obtendremos los operadores del segmento global de la pila
			op1 = globalSegment.removeLast();
			op2 = globalSegment.removeLast();
			res = op1 & op2;
			globalSegment.add(res);
		}
	}

	private void or(Instruccion i) {
		Integer op1, op2, res;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos
			// de las zona de datos locales de dicho RA
			op1 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			// Este get de los registros de activacion indexando con el registro
			// LB es equivalente a un getLast()
			op2 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			res = op1 | op2;
			activationRegs.get(registers[Registro.LB].getValue())
					.pushLocalData(res);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto
			// obtendremos los operadores del segmento global de la pila
			op1 = globalSegment.removeLast();
			op2 = globalSegment.removeLast();
			res = op1 | op2;
			globalSegment.add(res);
		}
	}

	private void inc(Instruccion i) {
		Integer op1, res;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos de las zona de datos locales de dicho RA
			op1 = activationRegs.get(registers[Registro.LB].getValue() - 1)
					.popLocalData();
			res = op1 + i.getSize();
			activationRegs.get(registers[Registro.LB].getValue() - 1)
					.pushLocalData(res);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto
			// obtendremos los operadores del segmento global de la pila
			op1 = globalSegment.removeLast();
			res = op1++;
			globalSegment.add(res);
		}
	}

	private void dec(Instruccion i) {
		Integer op1, res;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos de las zona de datos locales de dicho RA
			op1 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			res = op1--;
			activationRegs.get(registers[Registro.LB].getValue())
					.pushLocalData(res);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto
			// obtendremos los operadores del segmento global de la pila
			op1 = globalSegment.removeLast();
			res = op1--;
			globalSegment.add(res);
		}
	}

	private void neg(Instruccion i) {
		Integer op1, res;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos de las zona de datos locales de dicho RA
			op1 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			res = -op1;
			activationRegs.get(registers[Registro.LB].getValue())
					.pushLocalData(res);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto
			// obtendremos los operadores del segmento global de la pila
			op1 = globalSegment.removeLast();
			res = -op1;
			globalSegment.add(res);
		}
	}

	private void add(Instruccion i) {
		Integer op1, op2, res;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos
			// de las zona de datos locales de dicho RA
			op1 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			// Este get de los registros de activacion indexando con el registro
			// LB es equivalente a un getLast()
			op2 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			res = op1 + op2;
			activationRegs.get(registers[Registro.LB].getValue())
					.pushLocalData(res);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto
			// obtendremos los operadores del segmento global de la pila
			op1 = globalSegment.removeLast();
			op2 = globalSegment.removeLast();
			res = op1 + op2;
			globalSegment.add(res);
		}
	}

	private void sub(Instruccion i) {
		Integer op1, op2, res;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos
			// de las zona de datos locales de dicho RA

			op1 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			// Este get de los registros de activacion indexando con el registro
			// LB es equivalente a un getLast()
			op2 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			res = op1 - op2;
			activationRegs.get(registers[Registro.LB].getValue())
					.pushLocalData(res);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto
			// obtendremos los operadores del segmento global de la pila
			op1 = globalSegment.removeLast();
			op2 = globalSegment.removeLast();
			res = op1 - op2;
			globalSegment.add(res);
		}
	}

	private void mult(Instruccion i) {
		// System.out.println("**DEBUG** Primitiva 'mult'");
		Integer op1, op2, res;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos
			// de las zona de datos locales de dicho RA

			
			op1 = activationRegs.getLast().popLocalData();
			// Este get de los registros de activacion indexando con el registro
			// LB es equivalente a un getLast()
			op2 = activationRegs.getLast().popLocalData();
			res = op1 * op2;
			activationRegs.getLast().pushLocalData(res);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto obtendremos los operadores del segmento global de la pila
			op1 = globalSegment.removeLast();
			op2 = globalSegment.removeLast();
			res = op1 * op2;
			globalSegment.add(res);
		}
	}

	private void div(Instruccion i) {
		Integer op1, op2, res;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos
			// de las zona de datos locales de dicho RA
			op1 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			// Este get de los registros de activacion indexando con el registro
			// LB es equivalente a un getLast()
			op2 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			res = op1 / op2;
			activationRegs.get(registers[Registro.LB].getValue())
					.pushLocalData(res);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto
			// obtendremos los operadores del segmento global de la pila
			op1 = globalSegment.removeLast();
			op2 = globalSegment.removeLast();
			res = op1 / op2;
			globalSegment.add(res);
		}
	}

	private void mod(Instruccion i) {
		Integer op1, op2, res;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos
			// de las zona de datos locales de dicho RA
			op1 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			// Este get de los registros de activacion indexando con el registro
			// LB es equivalente a un getLast()
			op2 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			res = op1 % op2;
			activationRegs.get(registers[Registro.LB].getValue())
					.pushLocalData(res);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto
			// obtendremos los operadores del segmento global de la pila
			op1 = globalSegment.removeLast();
			op2 = globalSegment.removeLast();
			res = op1 % op2;
			globalSegment.add(res);
		}
	}

	private void lt(Instruccion i) {
		Integer op1, op2, res;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos
			// de las zona de datos locales de dicho RA
			op1 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			// Este get de los registros de activacion indexando con el registro
			// LB es equivalente a un getLast()
			op2 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			res = (op1 < op2) ? 1 : 0;
			activationRegs.get(registers[Registro.LB].getValue())
					.pushLocalData(res);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto
			// obtendremos los operadores del segmento global de la pila
			op1 = globalSegment.removeLast();
			op2 = globalSegment.removeLast();
			res = (op1 < op2) ? 1 : 0;
			globalSegment.add(res);
		}
	}

	private void le(Instruccion i) {
		Integer op1, op2, res;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos
			// de las zona de datos locales de dicho RA
			op1 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			// Este get de los registros de activacion indexando con el registro
			// LB es equivalente a un getLast()
			op2 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			res = (op1 <= op2) ? 1 : 0;
			activationRegs.get(registers[Registro.LB].getValue())
					.pushLocalData(res);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto
			// obtendremos los operadores del segmento global de la pila
			op1 = globalSegment.removeLast();
			op2 = globalSegment.removeLast();
			res = (op1 <= op2) ? 1 : 0;
			globalSegment.add(res);
		}
	}

	private void gt(Instruccion i) {
		Integer op1, op2, res;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos
			// de las zona de datos locales de dicho RA
			op1 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			// Este get de los registros de activacion indexando con el registro
			// LB es equivalente a un getLast()
			op2 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			res = (op1 > op2) ? 1 : 0;
			activationRegs.get(registers[Registro.LB].getValue())
					.pushLocalData(res);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto
			// obtendremos los operadores del segmento global de la pila
			op1 = globalSegment.removeLast();
			op2 = globalSegment.removeLast();
			res = (op1 > op2) ? 1 : 0;
			globalSegment.add(res);
		}
	}

	private void ge(Instruccion i) {
		Integer op1, op2, res;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos
			// de las zona de datos locales de dicho RA
			op1 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			// Este get de los registros de activacion indexando con el registro
			// LB es equivalente a un getLast()
			op2 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			res = (op1 >= op2) ? 1 : 0;
			activationRegs.get(registers[Registro.LB].getValue())
					.pushLocalData(res);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto
			// obtendremos los operadores del segmento global de la pila
			op1 = globalSegment.removeLast();
			op2 = globalSegment.removeLast();
			res = (op1 >= op2) ? 1 : 0;
			globalSegment.add(res);
		}
	}

	private void eq(Instruccion i) {
		Integer op1, op2, res;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos
			// de las zona de datos locales de dicho RA
			op1 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			// Este get de los registros de activacion indexando con el registro
			// LB es equivalente a un getLast()
			op2 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			res = (op1 == op2) ? 1 : 0;
			activationRegs.get(registers[Registro.LB].getValue())
					.pushLocalData(res);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto
			// obtendremos los operadores del segmento global de la pila
			op1 = globalSegment.removeLast();
			op2 = globalSegment.removeLast();
			res = (op1 == op2) ? 1 : 0;
			globalSegment.add(res);
		}
	}

	private void ne(Instruccion i) {
		Integer op1, op2, res;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos
			// de las zona de datos locales de dicho RA
			op1 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			// Este get de los registros de activacion indexando con el registro
			// LB es equivalente a un getLast()
			op2 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			res = (op1 != op2) ? 1 : 0;
			activationRegs.get(registers[Registro.LB].getValue())
					.pushLocalData(res);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto
			// obtendremos los operadores del segmento global de la pila
			op1 = globalSegment.removeLast();
			op2 = globalSegment.removeLast();
			res = (op1 != op2) ? 1 : 0;
			globalSegment.add(res);
		}
	}

	private void to21(Instruccion i) {
		Integer t1, t2_HighInt, t2_LowInt;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos
			// de las zona de datos locales de dicho RA

			// Si seguimos la convencion de que t1 fue el primer parametro
			// introducido en la pila y t2 el segundo, al primero que extraemos 
			// lo llamaremos por tanto t2
			
			// Se ha optado por un esquema Big-Endian, en el que el Integer más
			// significativo se introduce primero en la pila
			t2_LowInt = activationRegs.get(registers[Registro.LB].getValue())
					      .popLocalData();
			t2_HighInt = activationRegs.get(registers[Registro.LB].getValue())
						  .popLocalData();
			// Este get de los registros de activacion indexando con el registro
			// LB es equivalente a un getLast()
			t1 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			Integer t1_NewHighInt = new Integer(0);
			// Pusheamos en orden inverso
			activationRegs.get(registers[Registro.LB].getValue())
				.pushLocalData(t1_NewHighInt);
			activationRegs.get(registers[Registro.LB].getValue())
			   .pushLocalData(t1);
			activationRegs.get(registers[Registro.LB].getValue())
			   .pushLocalData(t2_HighInt);
			activationRegs.get(registers[Registro.LB].getValue())
			   .pushLocalData(t2_LowInt);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto
			// obtendremos los operadores del segmento global de la pila
			
			// XXX Lo mismo aqui
			t2_LowInt = globalSegment.removeLast();
			t2_HighInt = globalSegment.removeLast();
			t1 = globalSegment.removeLast();
			Integer t1_NewHighInt = new Integer(0);
			globalSegment.add(t1_NewHighInt);
			globalSegment.add(t1);
			globalSegment.add(t2_HighInt);
			globalSegment.add(t2_LowInt);
		}
	}

	private void to22(Instruccion i) {
		Integer t2;
		if (registers[Registro.LB].getValue() > 0) {
			// En el caso que estemos dentro de un registro de activacion,
			// popeamos de las zona de datos locales de dicho RA

			// Si seguimos la convencion de que t1 fue el primer parametro
			// introducido en la pila y t2 el segundo, al primero que extraemos 
			// lo llamaremos por tanto t2
			
			// Se ha optado por un esquema Big-Endian, en el que el Integer más
			// significativo se introduce primero en la pila
			t2 = activationRegs.get(registers[Registro.LB].getValue())
					.popLocalData();
			
			Integer t2_NewHighInt = new Integer(0);
			// Pusheamos en orden inverso
			activationRegs.get(registers[Registro.LB].getValue())
				.pushLocalData(t2_NewHighInt);
			activationRegs.get(registers[Registro.LB].getValue())
				.pushLocalData(t2);
		} else {
			// En caso contrario, nos encontramos en el nivel superior y por
			// tanto
			// obtendremos los operadores del segmento global de la pila
			
			// XXX Lo mismo aqui
			t2 = globalSegment.removeLast();
			Integer t2_NewHighInt = new Integer(0);
			globalSegment.add(t2_NewHighInt);
			globalSegment.add(t2);
		}
		
	}

	private void newar(Instruccion i) {
		if (registers[Registro.LB].getValue() > 0) {
			activationRegs.getLast().pushLocalData(heap.size());
		} else {
			globalSegment.add(heap.size());
		}
		for (int j = 0; j < i.getSize(); j++) {
			heap.add(0);
		}
	}

	private void free(Instruccion i) {
		int a;
		if (registers[Registro.LB].getValue() > 0) {
			a = activationRegs.getLast().popLocalData();		
		} else {
			a = globalSegment.removeLast();
		}
		for (int j = a; j < i.getSize(); j++) {
			heap.remove(j);
		}
	}

	private void get(Instruccion i) {
		final Integer HARDCODED = 7;
		Integer dato = new Integer(HARDCODED);
		Integer direccion;
		if (registers[Registro.LB].getValue() > 0) {
			direccion = activationRegs.getLast().popLocalData();
		} else {
			direccion = globalSegment.removeLast();
		}
		// Si apunta dentro del segmento global
		if (direccion <= globalSegment.size()) {
			globalSegment.add(direccion, dato);
//		} else {
			// XXX No hace falta de momento
			//for (int j = 1; j < registers[Registro.LB].getValue(); j++) {
			//	if (direccion <= activationRegs.get(j - 1).getLocalData())
			//}
		}
		
	}

	private void put(Instruccion i) {
		Integer dato;
		if (registers[Registro.LB].getValue() > 0) {
			dato = activationRegs.getLast().getLocalData().getLast();
			System.out.println("Llamada a put con variable local: " + dato);
		} else {
			dato = globalSegment.getLast();
			System.out.println("Llamada a put con variable global: " + dato);
		}
	}

	// -------------------------------------------
	// Implementacion de instrucciones
	// -------------------------------------------

	private void pushActivationReg() {
		// Creamos registro de activacion con direccion de retorno
		// correspondiente a la siguiente instruccion despues del call actual
		RegistroActivacion reg = new RegistroActivacion(
				(byte) (registers[Registro.CP].getValue() + 1));
		registers[Registro.LB].incValue();
		activationRegs.addLast(reg);
	}

	private RegistroActivacion popActivationReg() {
		registers[Registro.LB].decValue();
		return activationRegs.removeLast();
	}

	private void jumpif(Instruccion i) {

	}

	private void jumpi(Instruccion i) {
	
	}

	private void load(Instruccion i) {
		switch (i.getRegister()) {
		// Estamos en una subrutina, cargar en zona de datos locales
		case Registro.LB:
			if (i.getOffset() < 0) { // Tenemos que mirar en RAs anteriores
				for (int j = 2; j <= registers[Registro.LB].getValue(); j++) {
					RegistroActivacion reg = activationRegs.get(registers[Registro.LB].getValue() - j);
					if (Math.abs(i.getOffset()) <= reg.getLocalData().size()) {
						for (int k = 0; k < i.getSize(); k++) {
							Integer dato = reg.getLocalData().get(reg.getLocalData().size()
										- Math.abs(i.getOffset()) + k);
							activationRegs.getLast().pushLocalData(dato);
						}
						break;
					}
				}
			} else {
				Integer dato = activationRegs.getLast().getLocalData().get(i.getOffset());
				activationRegs.getLast().pushLocalData(dato);
			}
			break;
		case Registro.SB:
			Integer dato = globalSegment.get(globalSegment.size() - (i.getOffset() + 1));
			//globalSegment.add(dato);
			activationRegs.getLast().pushLocalData(dato);
			break;
		default: // heap?
			break;
		}

	}

	private void loada(Instruccion i) {
		switch (i.getRegister()) {
		case Registro.LB:
			Integer direccion = new Integer(i.getOffset());
			activationRegs.get(registers[Registro.LB].getValue() - 1).pushLocalData(direccion);
			break;
		case Registro.SB:
			// Integer dato = globalSegment.get(i.getOffset() + registers[Registro.SB].getValue());
			Integer direccion1 = i.getOffset() + registers[Registro.SB].getValue();
			if (registers[Registro.LB].getValue() > 0) {
				activationRegs.get(registers[Registro.LB].getValue() - 1).pushLocalData(direccion1);
			} else {
				globalSegment.addLast(direccion1);
			}
			break;
		default:
			break;
		}

	}

	private void loadi(Instruccion i) {

	}

	private void loadl(Instruccion i) {
		// Para el caso de querer introducir literales long, lo ideal sería que
		// eso no se controlara aquí sino desde el generador de código, para que
		// generase dos llamadas a esta funcion respetando el orden (endian-ness)
		// del literal long que se desea empujar a la pila
		if (registers[Registro.LB].getValue() > 0) {
			RegistroActivacion reg = activationRegs.getLast();
			reg.pushLocalData(new Integer(i.getOffset()));
		} else {
			globalSegment.addLast(new Integer(i.getOffset()));	
		}
	}

	private void store(Instruccion i) {
		ArrayList<Integer> datos = new ArrayList<Integer>();
		if (registers[Registro.LB].getValue() > 0) {
			for (int j = 0; j < i.getSize(); j++) {
				datos.add(0, activationRegs.getLast().popLocalData());
			}
		} else {
			for (int j = 0; j < i.getSize(); j++) {
				datos.add(0, globalSegment.removeLast());
			}
		}
		
		switch (i.getRegister()) {
		case Registro.LB:
			if (i.getOffset() > 0) {
				for (int j = 0; j < i.getSize(); j++) {
					activationRegs.getLast().getLocalData().add(i.getOffset() + j, datos.get(j));	
				}
			} // En caso necesario comprobar aquí si se quiere insertar en otros RAs anteriores
			break;
		case Registro.SB:
			for (int j = 0; j < i.getSize(); j++) {
				globalSegment.add(i.getOffset() + j, datos.get(j));
			}
			break;
		case Registro.HB:
			for (int j = 0; j < i.getSize(); j++) {
				heap.remove(i.getOffset());
				heap.add(i.getOffset() + j, datos.get(j));
			}
			break;
		default:
			break;
		}
	}

	private void storei(Instruccion i) {
		ArrayList<Integer> datos = new ArrayList<Integer>();
		Integer direccion;
		if (registers[Registro.LB].getValue() > 0) {
			for (int j = 0; j < i.getSize(); j++) {
				datos.add(j, activationRegs.getLast().popLocalData()); 
			}
			// Direccion
			direccion = activationRegs.getLast().popLocalData();
		} else {
			for (int j = 0; j < i.getSize(); j++) {
				datos.add(j, globalSegment.removeLast());
			}
			// Direccion
			direccion = globalSegment.removeLast();
		}
		
		if (direccion < globalSegment.size()) {
			for (int k = datos.size(); k >= 0; k--) {
				globalSegment.add(direccion, datos.get(k));
				direccion++;
			}
		} else {
			int l, temp = globalSegment.size();
			for (l = 0; l < registers[Registro.LB].getValue(); l++) {
				temp += activationRegs.get(l).getLocalData().size();
				if (temp >= direccion)
					break; 
			}
			for (int k = datos.size(); k >= 0; k--) {
				activationRegs.get(l).getLocalData().add(temp - direccion, datos.get(k));
				direccion++;
			}
		}
	}

	private void call(Instruccion i) {
		if (i.getRegister() == Registro.PB) { // Llamando a primitiva
			executePrimitive(i);
		} else {
			pushActivationReg();
			byte direccionSalto = (byte) (registers[i.getRegister()].getValue() + i
					.getOffset());
			registers[Registro.CP].setValue((byte) (direccionSalto - 1));
		}

	}

	private void calli(Instruccion i) {

	}

	private void returnar(Instruccion i) {
		RegistroActivacion reg = popActivationReg();
		// Eliminar parámetros en el RA anterior
		for (int j = 0; j < i.getOffset(); j++) {
			activationRegs.getLast().popLocalData();
		}
		// Reemplazarlos por las palabras adecuadas
		for (int k = 0; k < i.getSize(); k++) {
			activationRegs.getLast().pushLocalData(reg.popLocalData());
		}
		// El motivo de restar 1 a la direccion de retorno es para compensar
		// el incremento que se realiza en el dispatcher execute()
		registers[Registro.CP].setValue((byte) (reg.getReturnAddress() - 1));
	}

	private void push(Instruccion i) {
		//System.out.println("Dummy push");
		// XXX aqui me quede
		if (registers[Registro.LB].getValue() > 0) {
			for (int j = 0; j < i.getOffset(); j++) {
				activationRegs.getLast().pushLocalData(0);
			}
		} else {
			for (int j = 0; j < i.getOffset(); j++) {
				globalSegment.add(0);
			}
		}
	}

	private void pop(Instruccion i) {
		//System.out.println("Dummy pop");
		if (registers[Registro.LB].getValue() > 0) {
			for (int j = 0; j < i.getOffset(); j++) {
				activationRegs.getLast().popLocalData();
			}
		} else {
			for (int j = 0; j < i.getOffset(); j++) {
				globalSegment.removeLast();
			}
		}
	}

	private void jump(Instruccion i) {

	}

	private void halt(Instruccion i) {
		System.out.println("HALT: finaliza el programa");
		System.exit(0);
	}

	public void printCodeSegment() {
		for (int i = 0; i < codeSegment.size(); i++) {
			System.out.println(codeSegment.get(i).toString());
		}
	}
}
